package com.bridgeintelligent.tag.engine.mvc.strategy;

import com.bridgeintelligent.tag.engine.api.*;
import com.bridgeintelligent.tag.engine.api.rsp.MultiAnalysisAggregationResult;
import com.bridgeintelligent.tag.engine.core.cache.CacheConfig;
import com.bridgeintelligent.tag.engine.core.cache.CacheUtils;
import com.bridgeintelligent.tag.engine.es.base.ESQueryUtils;
import com.bridgeintelligent.tag.engine.es.model.AnalysisAggregationModel;
import com.bridgeintelligent.tag.engine.es.model.GroupItem;
import com.bridgeintelligent.tag.engine.es.query.EsQueryBuilder;
import com.bridgeintelligent.tag.engine.mvc.service.IGroupService;
import com.bridgeintelligent.tag.engine.parse.BaseParse;
import com.bridgeintelligent.tag.utils.FieldHandler;
import com.wayneleo.quickstart.framework.ExceptionCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.range.DateRangeAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;

/**
 * @Auther: xiang
 * @Date: 2022/5/22 15:53
 * @Description: AbstractAnalysisTemple
 * @Version 1.0.0
 */
@Slf4j
public abstract class AbstractAnalysisTemple {

    protected AnalysisAggregationModel model;

    protected IGroupService groupService;

    protected static String GROUP_FORMAT = "%s_%s";
    //校验数字
    protected static Pattern pattern = Pattern.compile("^(\\-|\\+)?\\d+(\\.\\d+)?$");

    private AbstractAnalysisTemple(){};

    public AbstractAnalysisTemple(AnalysisAggregationModel model,IGroupService groupService){
        this.model = model;
        this.groupService = groupService;
    }


    public abstract MultiAnalysisAggregationResult analysisHandler() throws IOException;


    public SearchResponse search(SearchRequest searchRequest) throws IOException {
        return ESQueryUtils.getClient().search(searchRequest, RequestOptions.DEFAULT);
    }

    /**
     * 数值类标签默认分组
     * @param field 要聚合的标签
     * @param dimId  要聚合的标签在ES里的真实字段
     * @param interval 间距
     * @return HistogramAggregationBuilder
     */
    public HistogramAggregationBuilder getNumberHistogramAggregation(String field, String dimId, Double interval){
        HistogramAggregationBuilder builder = AggregationBuilders
                .histogram("histogram_"+field)
                .field(dimId)
                .interval(interval)
                .order(BucketOrder.key(true))
                .minDocCount(1);
        return builder;
    }

    /**
     * 数值类标签自定义分组
     * @param field 要聚合的标签
     * @param dimId 要聚合的标签在ES里的真实字段
     * @param items 分组信息
     * @return RangeAggregationBuilder
     */
    public RangeAggregationBuilder getNumberRangeAggregation(String field, String dimId, List<GroupItem> items) {
        RangeAggregationBuilder rangeBuilder = AggregationBuilders
                .range("range_"+field).field(dimId);
        for(GroupItem item:items){
            String low = item.getLow();
            String high = item.getHigh();
            //不为空且不含无穷
            if (!StringUtils.isEmpty(low) && !StringUtils.isEmpty(high) && !low.contains("∞") && !high.contains("∞")){
                rangeBuilder.addRange(low+"~"+high,new Double(low),new Double(high));
            }else if (!StringUtils.isEmpty(low) && StringUtils.equals(low.trim(),"-∞")) {
                rangeBuilder.addUnboundedTo(low + "~" + high, new Double(high));
            }else if (!StringUtils.isEmpty(high) && (StringUtils.equals(high.trim(),"∞") || StringUtils.equals(high.trim(),"+∞"))){
                rangeBuilder.addUnboundedFrom(low+"~+∞",new Double(low));
            }
        }
        return rangeBuilder;
    }

    /**
     * 日期类标签默认分组
     * @param field 要聚合的标签
     * @param dimId 要聚合的标签在ES里的真实字段
     * @param dateInterval 间距
     * @return DateHistogramAggregationBuilder
     */
    public DateHistogramAggregationBuilder getDateHistogramAggregation(String field, String dimId, DateHistogramInterval dateInterval) {
        DateHistogramAggregationBuilder builder = AggregationBuilders
                .dateHistogram("histogram_"+field)
                .field(dimId)
                .dateHistogramInterval(dateInterval)
                .format("yyyy-MM-dd")
                .order(BucketOrder.key(true))
                .minDocCount(1);
        return builder;
    }

    /**
     * 日期类标签自定义分组
     * @param field 要聚合的标签
     * @param dimId 要聚合的标签在ES里的真实字段
     * @param items 分组信息
     * @return DateRangeAggregationBuilder
     */
    public DateRangeAggregationBuilder getDateRangeAggregation(String field, String dimId, List<GroupItem> items) {
        DateRangeAggregationBuilder rangeBuilder = AggregationBuilders
                .dateRange("range_"+field).field(dimId);
        for(GroupItem item:items){
            String low = item.getLow();
            String high = item.getHigh();
            //不为空且不含无穷
            if (!StringUtils.isEmpty(low) && !StringUtils.isEmpty(high) && !low.contains("∞") && !high.contains("∞")){
                low = LocalDate.parse(item.getLow(), DateTimeFormatter.BASIC_ISO_DATE).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                high = LocalDate.parse(item.getHigh(), DateTimeFormatter.BASIC_ISO_DATE).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                rangeBuilder.addRange(low + "/" + high,low,high);
            }else if (!StringUtils.isEmpty(low) && StringUtils.equals(low.trim(),"-∞")) {
                high = LocalDate.parse(item.getHigh(), DateTimeFormatter.BASIC_ISO_DATE).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                rangeBuilder.addUnboundedTo(low + "/" + high, high);
            }else if (!StringUtils.isEmpty(high) && (StringUtils.equals(high.trim(),"∞") || StringUtils.equals(high.trim(),"+∞"))){
                low = LocalDate.parse(item.getLow(), DateTimeFormatter.BASIC_ISO_DATE).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                rangeBuilder.addUnboundedFrom(low+"/+∞",low);
            }
        }
        return rangeBuilder;
    }

    /**
     * 标志型标签字典项分组
     * @param field 要聚合的标签
     * @param dimId 要聚合的标签在ES里的真实字段
     * @param classify 标签类型
     * @return TermsAggregationBuilder
     */
    public TermsAggregationBuilder getTermAggregation(String field, String dimId,String classify) {
        if (StringUtils.equals(classify,"01")){
            TermsAggregationBuilder termBuilder = AggregationBuilders
                    .terms("group_"+field).field(dimId).order(BucketOrder.count(false)).size(5);
            return termBuilder;
        }else{
            TermsAggregationBuilder termBuilder = AggregationBuilders
                    .terms("group_"+field).field(dimId).order(BucketOrder.count(false)).size(Integer.MAX_VALUE);
            return termBuilder;
        }
    }

    /**
     * 将builder 添加子类聚合
     * @param builder 源builder
     * @param model 目标聚合字段
     * @return
     */
    public AggregationBuilder  subAggregation(AggregationBuilder builder,AnalysisAggregationModel model){
        if (CollectionUtils.isNotEmpty(model.getFields())){
            model.getFields().forEach(e -> {
                builder.subAggregation(AggregationBuilders.max("max_"+e).field(model.getTrendMap().get(e)));
                builder.subAggregation(AggregationBuilders.min("min_"+e).field(model.getTrendMap().get(e)));
                builder.subAggregation(AggregationBuilders.avg("avg_"+e).field(model.getTrendMap().get(e)));
                builder.subAggregation(AggregationBuilders.sum("sum_"+e).field(model.getTrendMap().get(e)));
            });
        }
        return builder;
    }

    /**
     * 封装 QueryBuilder
     * @param filterGroup 查询条件
     * @return QueryBuilder
     */
    public QueryBuilder getQueryBuilder(FilterGroup filterGroup){
        return EsQueryBuilder.buildFilter(filterGroup);
    }

    /**
     * 封装 静态客群的QueryBuilder
     * @param customerId 静态客群ID
     * @return QueryBuilder
     */
    public QueryBuilder getStaticQueryBuilder(String customerId){
        return QueryBuilders.termsQuery(FieldHandler.CUSTOMER_FLAG,customerId);
    }

    /**
     * 根据key获取对应的索引信息
     * @param key 索引标识
     * @return 索引信息
     */
    public IndexNameInfo getIndexInfo(String key){
       return CacheUtils.getCacheInfo(CacheConfig.CONFIGURATION_INFO, key);
    }

    /**
     * 根据标签ID查询标签信息
     * @param dimId 标签ID
     * @return 标签信息
     */
    public DimInfo getDimInfo(String dimId){
        return (DimInfo) CacheUtils.getCacheInfo(CacheConfig.DIM_INFO, dimId);
    }

    /**
     * 根据标签项ID查询标签项信息
     * @param dimId 标签ID
     * @param itemId 标签项ID
     * @return 标签信息
     */
    public DimItemInfo getDimItemInfo(String dimId,String itemId){
        return (DimItemInfo) CacheUtils.getCacheInfo(CacheConfig.DIM_ITEM_INFO,String.format(BaseParse.GROUP_FORMAT, dimId, itemId));
    }

    /**
     * 计算真实field
     * @param field
     * @param date
     * @return
     */
    public String getRealField(String field, String date, Map<String, String> relation){
        return FieldHandler.addSuffixField(field,date,relation);
    }

    /**
     * 获取数值类的默认间距
     * @param dimId 标签ID
     * @param queryBuilder 查询条件
     * @return 间距值
     * @throws IOException
     */
    public Double getNumberInterval(String dimId,QueryBuilder queryBuilder,String indexId) throws IOException {
        Double interval = groupService.getNumberInterval(dimId, queryBuilder,indexId);
        if(interval==null){
            throw new EngineException(new ExceptionCode(999,"未获取到标签:"+dimId+" 的min、max值"));
        }
        return interval;
    }

    /**
     * 获取日期类的默认间距
     * @param dimId 标签ID
     * @param queryBuilder 查询条件
     * @return 间距值
     * @throws IOException
     */
    public DateHistogramInterval getDateInterval(String dimId,QueryBuilder queryBuilder,String indexId) throws IOException {
        DateHistogramInterval dateInterval = groupService.getDateInterval(dimId,queryBuilder,indexId);
        if(dateInterval==null){
            throw new EngineException(new ExceptionCode(999,"未获取到标签:"+dimId+" 的min、max值"));
        }
        return dateInterval;
    }

    /**
     * 数值类计算title分组信息
     * @param keyAsString
     * @param interval
     * @return
     */
    public String getNumberTitle(String keyAsString,Double interval){
            long key = Double.valueOf(keyAsString).longValue();
            long high = key + interval.longValue();
            return  key+"~"+high;
    }

    /**
     * 日期类计算title分组信息
     * @param keyAsString
     * @param dateInterval
     * @return
     */
    public String getDateTitle(String keyAsString,DateHistogramInterval dateInterval){
        if(dateInterval.equals(DateHistogramInterval.YEAR)){
            DateTime.parse(keyAsString, DateTimeFormat.forPattern("yyyy-MM-dd")).plusYears(1).toString("yyyy-MM-dd");
            return keyAsString+"/"+DateTime.parse(keyAsString, DateTimeFormat.forPattern("yyyy-MM-dd")).plusYears(1).toString("yyyy-MM-dd");
        }
        if(dateInterval.equals(DateHistogramInterval.MONTH)){
            DateTime.parse(keyAsString, DateTimeFormat.forPattern("yyyy-MM-dd")).plusMonths(1).toString("yyyy-MM-dd");
            return keyAsString+"/"+DateTime.parse(keyAsString, DateTimeFormat.forPattern("yyyy-MM-dd")).plusMonths(1).toString("yyyy-MM-dd");
        }
        String inter = dateInterval.toString();
        String start = keyAsString;
        if(StringUtils.startsWith(keyAsString, "-")){
            // 最小日期
            start = "0001-01-01";
        }
        return start+"/"+DateTime.parse(keyAsString, DateTimeFormat.forPattern("yyyy-MM-dd")).plusDays(Integer.parseInt(inter.substring(0, inter.length()-1))).toString("yyyy-MM-dd");
    }

    /**
     * 计算真实value值
     * @param valueStr
     * @return
     */
    public String realValue(String valueStr){
        valueStr = Optional.ofNullable(valueStr).orElse("0.00");
        if (pattern.matcher(valueStr).matches()){
            return String.valueOf(new BigDecimal(valueStr).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
        }else if(valueStr.contains("Infinity")){
            return "0.00";
        }else{
            return new BigDecimal(valueStr).toPlainString();
        }
    }

    /**
     * 计算百分比
     * @param k1
     * @param k2
     * @return
     */
    public String compute(long k1,long k2){
        BigDecimal n1 = BigDecimal.valueOf(k1);
        BigDecimal n2 = BigDecimal.valueOf(k2);
        if (k2 == 0l || k1 == 0l){
            return "0%";
        }
        return n1.multiply(new BigDecimal("100")).divide(n2,2,BigDecimal.ROUND_HALF_UP)+"%";
    }


    /**
     * Y维度根据title排序
     * @param classify 类型
     * @param data 数据
     */
    public void sortY(String classify, List<MultiAnalysisAggregationResult.Y> data) {
        if(StringUtils.equals(classify,"03")){
            data.sort((o1, o2) -> {
                Double v1 = getValue(o1.getTitle());
                Double v2 = getValue(o2.getTitle());
                return v1.compareTo(v2);
            });
        }
        if(StringUtils.equals(classify,"05")){
            data.sort((o1, o2) -> {
                String title1 = o1.getTitle();
                String title2 = o2.getTitle();
                if(StringUtils.contains(title1,"-∞")||StringUtils.contains(title2,"+∞")){
                    return -1;
                }
                if(StringUtils.contains(title1,"+∞")||StringUtils.contains(title2,"-∞")){
                    return 1;
                }
                return StringUtils.compare(title1, title2);
            });
        }
    }

    /**
     * X维度根据title排序
     * @param classify 类型
     * @param data 数据
     */
    public void sortX(String classify, List<MultiAnalysisAggregationResult.X> data) {
        if(StringUtils.equals(classify,"03")){
            data.sort((o1, o2) -> {
                Double v1 = getValue(o1.getTitle());
                Double v2 = getValue(o2.getTitle());
                return v1.compareTo(v2);
            });
        }
        if(StringUtils.equals(classify,"05")){
            data.sort((o1, o2) -> {
                String title1 = o1.getTitle();
                String title2 = o2.getTitle();
                if(StringUtils.contains(title1,"-∞")||StringUtils.contains(title2,"+∞")){
                    return -1;
                }
                if(StringUtils.contains(title1,"+∞")||StringUtils.contains(title2,"-∞")){
                    return 1;
                }
                return StringUtils.compare(title1, title2);
            });
        }
    }

    public Double getValue(String D1) {
        String[] array = D1.split("~");
        if(D1.indexOf("-")==0){
            if(StringUtils.equals("-∞",array[0])){
                return Double.MIN_VALUE;
            }
            return Double.parseDouble(array[0]);
        }else{
            if(StringUtils.equals("+∞",array[0]) || StringUtils.equals("∞",array[0])){
                return Double.MAX_VALUE;
            }
            return Double.parseDouble(array[0]);
        }
    }

    /**
     * 处理集合型标签title
     * @param key 中文名称
     * @param value 码值
     * @return
     */
    public String joinStr(String key,String value){
        StringBuffer buffer = new StringBuffer();
        buffer.append(key).append("(").append(value).append(")");
        return buffer.toString();
    }



}
